Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge max-next-v2016.07-1 into next #945

Merged
merged 22 commits into from
Jun 27, 2016
Merged

Conversation

eugeneia
Copy link
Member

petebristow and others added 20 commits April 14, 2016 21:08
Added a long description of the "feature cache" and how it supports
reconnecting to a running QEMU.

Added a restriction so that the cache is only considered for the first
feature negotiation performed by a vhost-user app instance. This is to
avoid using the cached features when renegotiating with a guest e.g.
when the guest has switched to a new driver. Not yet tested.
Now it is possible to request specific alignment for DMA memory.

This is practical. For example, Mellanox ConnectX-4 requires specific
alignments (e.g. 4KB).

(cherry picked from commit f349c41)

*Optional*. Start the app. At this the `input` and `output` link tables are
populated, and `appname` is set. The app can perform additional initialization
if required.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The details of this callback are surprising to me.

Based on the name start I would expect the callback to be run once when an app instance is first created and before any traffic is processed. Looking at the code it seems like start() is actually always called, on every app instances, each time you run engine.configure() (even if your new config is identical to the old one).

Could you please update either the name, the description, or the implementation to make the behavior & appropriate uses more obvious?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lukego @dpino What do you think about the name configured, e.g. the callback to be called after an app was configured?

@dpino
Copy link
Contributor

dpino commented Jun 16, 2016

@eugeneia @lukego I agree with Luke's point of view, the snippet of code is run every time configure is called so better call the event configure instead of start. However that's not what I originally intended. Originally the trigger of the event was from ops:start(), but when doing it from there the links are not set up at that point, so that makes start less useful. So I moved the code to after the links are setup, but that changes the semantic of the event as the code is called every time.

I'm OK with calling it configure, but as apps can implement stop I think having a start counterpart makes sense. If going for this solution, the start loop should only be called if ops.start was executed.

@eugeneia eugeneia closed this Jun 16, 2016
@eugeneia eugeneia reopened this Jun 16, 2016
@eugeneia
Copy link
Member Author

eugeneia commented Jun 16, 2016

Ugh I fat-fingered the close button, sorry!

@dpino I would say new is the counterpart to stop. I think configure makes sense, as this callback lets apps react to the way they were configured.

@dpino
Copy link
Contributor

dpino commented Jun 16, 2016

@eugeneia Taking a look at Alexander's patch I think it makes sense to call it configure too, as he originally called that method post_config. Agree with the name change.

@lukego
Copy link
Member

lukego commented Jun 17, 2016

Thanks for the name change!

I would still like to address these issues before merge:

  • Exactly when is configure called and what are the intended uses? Seems to me like the implementation is a hook for every time engine.configure() is called independent of the content of the configuration changes and that seems potentially excessive to me (more often than I would expect from reading the documentation).
  • Do we really want to have both configure and reconfig callbacks? This is the situation on the submitted branch. I think we should either merge these together or otherwise clearly explain when to use one vs the other. (If we keep both then we should also be consistent about abbreviating their names or not i.e. config vs configure.)

@lukego
Copy link
Member

lukego commented Jun 17, 2016

I would like to please withdraw my objection to the implementation of configure.

I checked how the relink() callback had been implemented (added in commit 4f166b3 and removed in commit c4a7885) and it is exactly the same. Could be seen as premature optimization to make a more complex implementation that runs the callback less frequently e.g. skipping for apps whose own links and configurations have not been touched.

So my only request now is to please reconcile configure with reconfig either by merging them together somehow or making their names/purposes more clearly distinct :).

@dpino
Copy link
Contributor

dpino commented Jun 17, 2016

I think I can explain best what I need if I describe the use cases I'm trying to fix.

I would like to give apps a chance to complete their initialization once they have been configured (with their links setup too). app:reconfig is only called if an app already exists in the app_array so it cannot be used.

One example that could take advantage of this new event is the NDP app in the lwAFTR.

https://github.com/Igalia/snabb/blob/lwaftr/src/apps/lwaftr/ipv6_apps.lua#L186

There's a case where the NDP app needs to send Neighbour Solicitation packets until a parameter is resolved (self.dst_eth). At this moment, we are implementing this logic in the push method, because only at that point links are setup. However, we could start sending NS packets as soon as the app and the links are ready.

It could be argued that the links won't be processed until push is called in any case, but putting this logic in two different methods would make the code cleaner and the push method less bloated IMHO.

Another use case is creating counters by app name. An app name is added to an app on start, but since there's no start callback apps can react to, the app has to wait until push to create its counters and it has to do it only once. Something like:

function App:push ()
    if not self.added_counters then
       self.counter = counter.open(self.appname.."/counter")
       self.added_counters = true
   end
   local input, output = assert(self.input.input), assert(self.output.output)
   while not link.empty(input) do
      ...
   end
end

I think a method where apps could further initialize themselves (in new appname or links do not exist), seems necessary.

Solutions I can think of:

  • We could discard this new configure method and call reconfig on start too, but that would make the reconfig and startstates less clear. In addition to that, the first time reconfig is called, links wouldn't exist. I doesn't seem like a good solution :(
  • I go back to my original request of adding a start event (I agree having a configure and reconfig callbacks would be confusing). I wanted to defer the call of the event until the links were created, but I didn't do it right. Should be something like:
function apply_config_actions (actions, conf)
   ...
   local started_apps = {}
   function ops.start (name)
      ...
      table.insert(started_apps, app)
   end
   -- Setup links.
   ...
   for _, app in ipairs(started_apps) do
      if app.start then app:start() end
   end
end

@lukego
Copy link
Member

lukego commented Jun 20, 2016

@diego Thanks for providing that context. That helps a lot.

How about implementing this all locally in the push method like this?

function NDP:push ()
   self:maybe_send_ns()
   ... process packets ...
end

-- Send rate-limited neighbor solicitation message.
function NDP:maybe_send_ns ()
   self.next_ns_time = self.next_ns_time or engine.now() -- first message is immediate
   if self.next_ns_time <= engine.now() then
      self:send_ns()
      self.next_ns_time = engine.now() + self.ns_interval
   end
end

This seems neat, predictable, and non-bloated to me. The send_ns() function will be called at regular intervals and take the appropriate (non-)action based on the current links and configuration. How about it?

Note that lib.timer should not be used from apps at all and this seems to be the root of our difficulty here. Those timers are persistent, like crontab entries, and can easily drift out of sync with the app's own lifetime and configuration. You don't want zombie timers accumulating and running in the background and referencing obsolete objects via closures. I suspect that the root of our whole difficulty here is trying to match the lifecycles of apps and lib.timer timers which don't align very well. (Maybe we should rename lib.timer to lib.cron to make its appropriate usage more obvious?)

WDYT?

Incidentally I would quite like to have a tick() timer in apps that runs at a fixed frequency e.g. 1 millisecond. This could be used for taking care of administrivia such as checking whether it is time to send a neighbor solicitation message. This is something we could add if it would simplify or speed up existing apps in practice.

@lukego lukego mentioned this pull request Jun 20, 2016
@dpino
Copy link
Contributor

dpino commented Jun 20, 2016

@lukego Thanks for the detailed explanation, I like this new design more. In fact, before I needed to add a timer.deactivate method which I can remove now. I was not aware that apps should not depend on timers to implement their logic.

@eugeneia
Copy link
Member Author

@lukego Last commit renames and documents the callback as per #946.

@lukego lukego merged commit e60226c into snabbco:next Jun 27, 2016
@lukego
Copy link
Member

lukego commented Jun 27, 2016

Merged. Thanks and sorry about the wait!

dpino added a commit to dpino/snabb that referenced this pull request Sep 18, 2017
Missing statements in snabb-softwire-v2 schema
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants